﻿using MethodBoundaryAspect.Fody.Attributes;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Seagull.Aop
{

    /// <summary>
    /// 缓存拦截器，当在缓存有效期内，直接返回缓存结果，不再执行该函数。
    /// (使用前请确保注入了IMemoryCache服务)
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class CacheAttribute : OnMethodBoundaryAspect
    {
        private readonly TimeSpan _timeSpan;
        private readonly IMemoryCache? _cache;
        private readonly ICacheParameterInterpreter _interpreter;


        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="hours">小时数</param>
        /// <param name="minutes">分钟数</param>
        /// <param name="seconds">秒数</param>
        public CacheAttribute(int hours, int minutes, int seconds) : this(new TimeSpan(hours, minutes, seconds))
        {
        }

        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="days">天数</param>
        /// <param name="hours">小时数</param>
        /// <param name="minutes">分钟数</param>
        /// <param name="seconds">秒数</param>
        public CacheAttribute(int days, int hours, int minutes, int seconds) : this(new TimeSpan(days, hours, minutes, seconds))
        {
        }

        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="hours">小时数</param>
        /// <param name="minutes">分钟数</param>
        /// <param name="seconds">秒数</param>
        /// <param name="interpreter">参数解释器的对象类型</param>
        public CacheAttribute(int hours, int minutes, int seconds, Type interpreter) : this(new TimeSpan(hours, minutes, seconds), interpreter)
        {
        }

        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="days">天数</param>
        /// <param name="hours">小时数</param>
        /// <param name="minutes">分钟数</param>
        /// <param name="seconds">秒数</param>
        /// <param name="interpreter">参数解释器的对象类型</param>
        public CacheAttribute(int days, int hours, int minutes, int seconds, Type interpreter) : this(new TimeSpan(days, hours, minutes, seconds), interpreter)
        {
        }

        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="timeSpan"></param>
        private CacheAttribute(TimeSpan timeSpan) : this(timeSpan, typeof(ICacheParameterInterpreter))
        {
        }

        /// <summary>
        /// 配置缓存参数
        /// </summary>
        /// <param name="timeSpan"></param>
        /// <param name="interpreter"></param>
        private CacheAttribute(TimeSpan timeSpan, Type interpreter)
        {
            _timeSpan = timeSpan;
            if (App._provider is not null)
            {
                _cache = App.HttpContext.RequestServices.GetService<IMemoryCache>();
                var instance = App.HttpContext.RequestServices.GetService(interpreter);
                if (instance is null)
                {
                    var ctor = interpreter.GetConstructor(Array.Empty<Type>());
                    if (ctor is not null)
                    {
                        instance = ctor.Invoke(null);
                    }
                }
                if (instance is ICacheParameterInterpreter parameterInterpreter)
                {
                    _interpreter = parameterInterpreter;
                }
            }
        }

        /// <summary>
        /// 进入函数前执行的判断，如果存在缓存，则直接返回缓存数据。
        /// </summary>
        /// <param name="args"></param>
        /// <exception cref="NullReferenceException"></exception>
        public override void OnEntry(MethodExecutionArgs args)
        {
            MethodInfo method = (MethodInfo)args.Method;
            if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) return;

            if (_cache is null)
                throw new NullReferenceException("没有找到 Microsoft.Extensions.Caching.Memory.IMemoryCache 服务。");

            object key = _interpreter.Key(args.Instance, args.Method, args.Arguments);
            if (_cache.TryGetValue(key, out var value))
            {
                if (method.GetCustomAttribute<AsyncStateMachineAttribute>() is not null)
                {
                    Type type = method.ReturnType.GenericTypeArguments[0];
                    value = this.GetType().GetMethod(nameof(ReturnValueConversionGenericTask), BindingFlags.NonPublic | BindingFlags.Instance)!.MakeGenericMethod(type).Invoke(this, new object[] { value });
                }
                args.ReturnValue = value;
                args.FlowBehavior = FlowBehavior.Return;
            }
        }

        /// <summary>
        /// 将结果转换为Task`1`对象。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="returnValue"></param>
        /// <returns></returns>
        private async Task<T> ReturnValueConversionGenericTask<T>(T returnValue)
        {
            return await Task.Run(() => returnValue);
        }

        /// <summary>
        /// 退出函数是执行的条件，将得到的结果进行缓存。
        /// </summary>
        /// <param name="args"></param>
        public override void OnExit(MethodExecutionArgs args)
        {
            if (args.ReturnValue is Task t)
                t.ContinueWith(task => Exit(args));
            else
                Exit(args);
        }

        private void Exit(MethodExecutionArgs args)
        {
            MethodInfo method = (MethodInfo)args.Method;
            if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) return;

            object key = _interpreter.Key(args.Instance, args.Method, args.Arguments);
            object value = args.ReturnValue;
            if (method.GetCustomAttribute<AsyncStateMachineAttribute>() is not null && args.ReturnValue is Task)
            {
                value = ((dynamic)value).Result;
            }
            if (_timeSpan == TimeSpan.Zero)
                _cache.Set(key, value);
            else
                _cache.Set(key, value, _timeSpan);
        }
    }
}
